W11. Интерфейс и реализация, final-методы и final-классы, интерфейсы, перечисления, Java Collections Framework
1. Резюме
1.1 Проектирование интерфейса и реализации
Проектируя ОО-программы, важно решить: от базового класса наследовать только interface (сигнатуры методов) или ещё и implementation (готовый код)? От этого зависит сопровождаемость и устойчивость к логическим ошибкам.
1.1.1 Проблема наследования реализации
Пусть несколько моделей самолёта должны реализовать fly(). Если сделать конкретный базовый класс Airplane с реализацией fly() по умолчанию, все подклассы унаследуют этот код автоматически:
class Airplane {
public void fly() {
// Standard Airbus flying algorithm
}
}
class AirbusA extends Airplane { }
class AirbusB extends Airplane { }
class Boeing extends Airplane { }Тогда Boeing неожиданно использует алгоритм полёта Airbus! Синтаксис корректен, компилятор молчит, а ошибка semantic — неверное поведение наследуется незаметно.
1.1.2 Решение: разделить interface и implementation
Правильный путь — разделить interface и implementation через абстрактные классы и методы:
abstract class Airplane {
public abstract void fly(); // Interface only
protected void defaultFly() { // Optional default implementation
// Standard flying algorithm
}
}
class AirbusA extends Airplane {
public void fly() { defaultFly(); } // Uses default
}
class Boeing extends Airplane {
public void fly() {
// Boeing's own flying algorithm
}
}Теперь компилятор заставляет каждый подкласс явно реализовать fly(). Модели Airbus могут вызвать общий defaultFly(), а Boeing обязан написать свой код — «тихого» наследования чужого поведения нет.
1.1.3 Рекомендации для базовых классов
При проектировании базового класса полезны такие правила:
- Только interface: метод
abstract, если подклассы обязаны дать свою реализацию; общий код спрячьте во вспомогательный метод вродеdefaultFly().
- Interface + implementation: метод virtual (в C++/C# явно, в Java — по умолчанию), если нужна реализация по умолчанию с возможностью переопределения.
- Фиксированное поведение: non-virtual (C++/C#) или
final(Java), если подклассы не должны менять семантику.
1.2 Final-методы
Final methods — методы, которые нельзя переопределить в подклассах. Два главных эффекта: guaranteed behavior и performance optimization.
1.2.1 Запрет переопределения
Ключевое слово final не даёт подклассу изменить поведение метода:
class Base {
public final void method() {
System.out.println("Base's method");
}
}
class Derived extends Base {
public void method() { // ERROR: Cannot override final method
System.out.println("Derived's method");
}
}Так method() всегда ведёт себя как в Base, независимо от динамического типа ссылки.
1.2.2 Эффект для производительности
final способствует early binding (static dispatch) вместо late binding (dynamic dispatch):
- Late binding: какой метод вызвать, решается в runtime по динамическому типу; есть накладные расходы (поиск в vtable).
- Early binding: выбор по статическому типу на этапе компиляции; быстрее.
Если метод final, компилятор знает, что переопределений нет, и может:
- генерировать более эффективные вызовы;
- inline — встроить тело метода в место вызова для мелких методов.
1.3 Final-классы
Final class нельзя расширять (subclass). Это удобно, когда наследование для класса нужно полностью запретить.
final class Base {
// Class implementation
}
class Derived extends Base { // ERROR: Cannot subclass final class
}Ключевые свойства:
- у
final-класса все методы неявноfinal;
- класс не может быть одновременно
abstractиfinal— это противоречие: абстрактный класс должен расширяться.
1.4 Интерфейсы
Interface — чистая абстракция: контракт поведения без реализации. Это другой акцент в ОО-дизайне: что объекты умеют делать, а не что они «есть».
1.4.1 Два взгляда на предметную область
- Class-based approach: объекты со состоянием и связями; акцент на «что это за вещь».
- Interface-based approach: сущности задаются поведением («что умеют»).
Интерфейсы воплощают второй подход и дают более чистую альтернативу множественному наследованию реализации.
1.4.2 Объявление интерфейса
Интерфейс задаёт только сигнатуры методов:
interface Features {
int numOfLegs();
boolean canFly();
boolean canSwim();
}Свойства интерфейсов:
- у методов нет тел (реализацию пишут классы);
- методы неявно
publicиabstract;
- нельзя создать экземпляр интерфейса (
new);
- нельзя иметь поля экземпляра (только константы);
- это контракт: класс, реализующий интерфейс, обязан предоставить все методы.
1.4.3 Реализация интерфейса
Класс реализует интерфейс ключевым словом implements:
class Lion implements Features {
public int numOfLegs() { return 4; }
public boolean canFly() { return false; }
public boolean canSwim() { return true; }
}
Features f = new Lion(); // Treat Lion as a set of features
if (f.canFly()) { /* ... */ }Класс обязан реализовать все методы интерфейса, иначе код не скомпилируется.
1.4.4 Несколько интерфейсов
Класс может реализовать несколько интерфейсов — это безопаснее множественного наследования реализации:
class Person implements iBodyParams, iSkills, iRelations {
// Must implement all methods from all three interfaces
}
Person john = new Person();
iSkills johnsSkills = john; // View John as a set of skillsТак один объект можно рассматривать под разными углами (facets) в зависимости от контекста.
1.4.5 Наследование интерфейсов
Интерфейсы могут расширять другие интерфейсы:
interface SpeedFeatures {
float maxSpeed();
float maxAcceleration();
}
interface EngineFeatures extends SpeedFeatures {
float numOfCyls();
float enginePower();
}
class Car implements EngineFeatures {
// Must implement methods from both interfaces
}Класс может унаследовать реализацию методов интерфейса от предка:
interface HasLegs {
int noLegs();
}
class Mammal implements HasLegs {
public int noLegs() { return 4; }
}
class Lion extends Mammal {
// Inherits the noLegs() implementation
}1.4.6 Интерфейсы вместе с наследованием классов
Интерфейсы и наследование классов комбинируются:
interface ColorFeatures {
Color color();
Border border();
}
abstract class Shape {
abstract void draw();
}
class Rectangle extends Shape {
void draw() { /* ... */ }
}
class ColoredRectangle extends Rectangle implements ColorFeatures {
// Inherits from Rectangle AND implements ColorFeatures
public Color color() { /* ... */ }
public Border border() { /* ... */ }
}Интерфейсы orthogonal к наследованию классов — это отдельное измерение абстракции.
1.4.7 Проверка типов с интерфейсами
Оператор instanceof работает и с интерфейсами:
interface Printable { void print(); }
interface Movable { void move(); }
class Rectangle extends Shape implements Printable, Movable {
// Implementation
}
Shape a = new Rectangle();
if (a instanceof Printable) {
((Printable)a).print();
}
if (a instanceof Movable) {
((Movable)a).move();
}Интерфейс может быть пустым — маркером / marker interface для свойств объекта.
1.4.8 Вложенные интерфейсы
Интерфейсы можно вкладывать в классы:
class SomeClass {
public interface Nested {
boolean isNotNegative(int x);
}
}
class MyClass implements SomeClass.Nested {
public boolean isNotNegative(int x) {
return x >= 0;
}
}
SomeClass.Nested obj = new MyClass();1.4.9 Интерфейсы как facets
Интерфейсы задают разные facets / views одного объекта — разные «контракты» для разных клиентов одной реализации.
1.5 Интерфейсы и абстрактные классы
Различие между interface и abstract class — базовый навык.
Сходства:
- оба задают абстракцию;
- напрямую не инстанцируются.
Различия:
- Interface: абстракция поведения — что объект умеет, а не как и какое у него состояние. (В современной Java есть
default-методы и константы, но смысл остаётся поведенческим.)
- Abstract class может содержать:
- объявления абстрактных методов;
- конкретные методы (поведение);
- поля экземпляра (состояние).
- объявления абстрактных методов;
Когда что выбирать:
- interfaces — общая способность/роль, которую могут разделять несвязанные классы;
- abstract classes — общее состояние и поведение для тесно связанной иерархии.
1.6 Перечисления (enum)
Enumerations (enum) — тип с фиксированным набором именованных констант; читабельнее и типобезопаснее «магических» целых.
1.6.1 Зачем enum
Состояния светофора часто моделируют целыми константами:
final int GREEN = 0;
final int YELLOW = 1;
final int RED = 2;
int trafficLight = GREEN;
trafficLight = 777; // Nothing prevents this!Проблема: откуда взялись именно эти числа? Что мешает записать 777?
Подход с enum:
enum Lights {
GREEN,
YELLOW,
RED
}
Lights tl = Lights.GREEN;
tl = 777; // Compiler ERROR: type mismatchКомпилятор обеспечивает типобезопасность: в tl нельзя подставить произвольное число.
1.6.2 Базовые перечисления
Набор именованных констант (enumerators):
enum Compass {
NORTH,
SOUTH,
EAST,
WEST
}
enum Day {
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
}Замечание: имена констант традиционно в UPPERCASE (наследие C/ассемблера), но это соглашение, не требование языка.
Внутри JVM константы связаны с порядковыми номерами, начиная с 0 (SUNDAY = 0, MONDAY = 1, …), но на этом уровне обычно не программируют.
1.6.3 Использование enum
С switch enum сочетается естественно:
public void tellItLikeItIs(Day day) {
switch (day) {
case MONDAY:
System.out.println("Mondays are bad.");
break;
case FRIDAY:
System.out.println("Fridays are better.");
break;
default:
System.out.println("Midweek days are so-so.");
break;
}
}1.6.4 Расширенные возможности enum в Java
В Java enum существенно богаче, чем в C/C++: enum — это полноценные классы. Отсюда следуют возможности:
1. Поля и конструкторы:
enum Coin {
PENNY(1), NICKEL(5), DIME(10), QUARTER(25);
private final int value;
Coin(int value) {
this.value = value;
}
}
Coin c = Coin.DIME; // c is associated with value 10Каждую константу можно инициализировать параметрами; конструктор вызывается при загрузке типа enum.
2. Методы у enum:
enum Coin {
PENNY(1), NICKEL(5), DIME(10), QUARTER(25);
private final int value;
Coin(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
Coin c = Coin.DIME;
int v = c.getValue(); // Returns 103. Стандартные методы enum:
У любого enum есть:
values(): массив всех констант в порядке объявления
enum Season { WINTER, SPRING, SUMMER, FALL }
for (Season s : Season.values()) {
System.out.println(s);
}
// Output: WINTER, SPRING, SUMMER, FALLordinal(): индекс константы (с нуля)
for (Season s : Season.values()) {
System.out.println(s.ordinal());
}
// Output: 0, 1, 2, 34. Тела у отдельных констант:
Каждая константа может переопределять методы:
enum Operation {
PLUS { double eval(double x, double y) { return x + y; } },
MINUS { double eval(double x, double y) { return x - y; } },
TIMES { double eval(double x, double y) { return x * y; } },
DIVIDE { double eval(double x, double y) { return x / y; } };
abstract double eval(double x, double y);
}
// Usage:
for (Operation op : Operation.values()) {
System.out.println("2.0 " + op + " 4.0 = " + op.eval(2.0, 4.0));
}
// Output:
// 2.0 PLUS 4.0 = 6.0
// 2.0 MINUS 4.0 = -2.0
// 2.0 TIMES 4.0 = 8.0
// 2.0 DIVIDE 4.0 = 0.55. Порядок вызова конструкторов:
Конструкторы enum вызываются при первой «загрузке» типа:
enum Color {
RED, GREEN, BLUE;
private Color() {
System.out.println("Constructor called for: " + this.toString());
}
}
Color c = Color.RED;
// Output:
// Constructor called for: RED
// Constructor called for: GREEN
// Constructor called for: BLUEВсе константы создаются при первом обращении к типу enum.
1.7 Java Collections Framework
Java Collections Framework — единая архитектура для хранения и обработки наборов объектов; избавляет от ручной реализации типовых структур и даёт стандартные абстракции.
Коллекции хранят reference type (объекты). Размер динамический — не нужно вручную расширять массивы.
1.7.1 Основные интерфейсы коллекций
Три ключевых вида:
1. List: упорядоченная коллекция с доступом по индексу; дубликаты разрешены.
2. Set: коллекция уникальных элементов без гарантированного порядка; дубликаты игнорируются; не более одного null.
3. Map: отображение keys → values; каждый ключ — не более одного значения; разные ключи могут указывать на одно значение.
1.7.2 Иерархия коллекций
Иерархия JCF:
Collection(root interface)List(ordered, indexed)ArrayList(resizable array)LinkedList(doubly-linked list)Vector(synchronized array)Stack(LIFO stack)
Set(unique elements)HashSet(hash table)LinkedHashSet(hash table + insertion order)SortedSet(sorted set)TreeSet(red-black tree)
Queue(FIFO queue)Deque(double-ended queue)LinkedList(also implements Deque)
Map(separate hierarchy, not part ofCollection)HashMap(hash table)LinkedHashMap(hash table + insertion order)Hashtable(legacy synchronized hash table)SortedMap(sorted map)TreeMap(red-black tree)
1.7.3 Списки (List)
List — упорядоченная коллекция: порядок элементов совпадает с порядком добавления; доступ по целому индексу.
Создание и пример:
import java.util.List;
import java.util.ArrayList;
List<String> list = new ArrayList<>();
list.add("Geeks");
list.add("Geeks");
list.add(1, "For"); // Insert at index 1
System.out.println(list); // [Geeks, For, Geeks]Часто используемые методы List:
int size(): число элементов
boolean isEmpty(): пуст ли список
void clear(): очистить
boolean add(E element): добавить в конец
void add(int index, E element): вставить по индексу
E get(int index): элемент по индексу
E set(int index, E element): заменить элемент
E remove(int index): удалить по индексу
boolean remove(Object o): удалить первое вхождение объекта
Ключевые свойства:
- дубликаты разрешены;
- сохраняется порядок вставки;
- доступ по позиции.
1.7.4 Множества (Set)
Set — коллекция без дубликатов; не более одного null; порядок не гарантирован (кроме специализированных реализаций).
Создание и пример:
import java.util.Set;
import java.util.HashSet;
Set<String> set = new HashSet<>();
set.add("Geeks");
set.add("For");
set.add("Geeks"); // Duplicate, will be ignored
set.add("Example");
set.add("Set");
System.out.println(set); // Order not guaranteed: [Geeks, Example, Set, For]Часто используемые методы Set:
int size(),boolean isEmpty(),void clear()
boolean add(E element)—false, если элемент уже был
boolean remove(Object o),boolean contains(Object o)
Ключевые свойства:
- без дубликатов;
- порядок не фиксирован (если не
LinkedHashSet/TreeSet);
- быстрая проверка принадлежности.
Сравнение реализаций Set:
| Реализация | Порядок | Сложность (поиск) | Структура |
|---|---|---|---|
HashSet |
нет | \(O(1)\) | Хеш-таблица |
LinkedHashSet |
порядок вставки | \(O(1)\) | Хеш-таблица + список |
TreeSet |
по возрастанию | \(O(\log n)\) | Красно-чёрное дерево |
1.7.5 Отображения (Map)
Map хранит пары ключ–значение: ключ уникален, значения могут повторяться.
Создание и пример:
import java.util.Map;
import java.util.HashMap;
Map<String, Integer> map = new HashMap<>();
map.put("a", 100);
map.put("b", 200);
map.put("c", 300);
map.put("a", 150); // Replaces previous value for key "a"
// Traversing the map
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}Часто используемые методы Map:
int size(),boolean isEmpty(),void clear()
V put(K key, V value)— возвращает старое значение
V get(K key)— илиnull
V remove(K key)
boolean containsKey,boolean containsValue
Set<K> keySet(),Collection<V> values(),Set<Map.Entry<K,V>> entrySet()
Ключевые свойства:
- один ключ → не более одного значения;
- ключи уникальны;
- значения могут совпадать;
- быстрый поиск по ключу.
Сравнение реализаций Map:
| Реализация | Порядок | null в значении |
Потокобезопасность | Сложность (поиск) | Структура |
|---|---|---|---|---|---|
HashMap |
нет | да | нет | \(O(1)\) | Хеш-таблица |
Hashtable |
нет | нет | да | \(O(1)\) | Хеш-таблица |
TreeMap |
по ключу | да/нет* | нет | \(O(\log n)\) | Красно-чёрное дерево |
*TreeMap: null в значении допустим (в зависимости от версии/компаратора), в ключах — нет.
List vs Set vs Map:
| List | Set | Map |
|---|---|---|
| доступ по индексу | без дубликатов | пары ключ–значение |
| дубликаты | повторный add игнорируется |
ключи уникальны |
| порядок вставки | порядок не гарантирован | поиск значения по ключу |
1.7.6 Iterator
Iterator — объект обхода коллекции; единообразный способ пройти по любой Collection.
Пример с Iterator:
import java.util.Iterator;
import java.util.HashSet;
import java.util.Set;
Set<Integer> set = new HashSet<>();
set.add(5);
set.add(1);
set.add(3);
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
Integer value = iterator.next();
System.out.print(value + " ");
}Методы Iterator:
boolean hasNext()
E next()
void remove()— удаляет элемент, последний возвращённыйnext()
С ArrayList:
import java.util.ArrayList;
import java.util.Iterator;
ArrayList<String> cars = new ArrayList<>();
cars.add("Volvo");
cars.add("BMW");
cars.add("Ford");
cars.add("Mazda");
Iterator<String> it = cars.iterator();
System.out.println(it.next()); // Volvoiterator() есть у любой коллекции, реализующей Collection.
1.8 UML-диаграммы классов
UML Class Diagrams наглядно показывают классы, поля, методы и связи в ОО-системе; полезны при проектировании и обсуждении архитектуры до кода.
1.8.1 Обозначение класса
Прямоугольник из трёх секций:
- имя класса
- атрибуты (поля) с видимостью и типом
- методы с видимостью, параметрами и типом результата
Видимость:
+: public-: private#: protected~: package-private
1.8.2 Связи
1. Association: общая связь «класс использует другой класс»
- сплошная линия
- кратность:
1,*,0..1,1..*, …
2. Navigable Association: направленная связь
- сплошная линия со стрелкой
3. Inheritance (Generalization): отношение «is-a»
- сплошная линия с пустым треугольником к надклассу
4. Realization / Implementation: класс реализует интерфейс
- пунктир с пустым треугольником к интерфейсу
5. Dependency: слабая зависимость одного класса от другого
- пунктир со стрелкой
6. Aggregation: «has-a», часть может существовать отдельно
- сплошная линия с пустым ромбом со стороны контейнера
7. Composition: «has-a», часть не существует отдельно от целого
- сплошная линия с закрашенным ромбом
Пример:
University (1) ---- (1..*) Department
Department (1) <>---- (1..*) Professor [aggregation]
Professor ---|> Person [inheritance]
На схеме:
- у университета много кафедр;
- кафедра aggregates множество профессоров;
ProfessorнаследуетPerson.
2. Определения
- Interface (как абстракция контракта): публичные сигнатуры методов класса — что можно делать с объектами этого класса.
- Implementation: фактический код, задающий работу методов: алгоритмы, структуры данных и внутренняя логика.
- Abstract Method: метод без тела, который обязаны реализовать подклассы.
- Abstract Class: класс, который нельзя инстанцировать напрямую; может содержать абстрактные методы; задаёт общий интерфейс и частичную реализацию для иерархии.
- Final Method: метод, который нельзя переопределить в подклассах; объявляется с ключевым словом
finalв Java. - Final Class: класс, который нельзя расширять (subclass); объявляется с
final. - Late Binding (Dynamic Dispatch): выбор метода во время выполнения по динамическому типу объекта; для виртуальных / переопределяемых методов.
- Early Binding (Static Dispatch): выбор метода на этапе компиляции по статическому типу; для non-virtual методов,
finalиstatic. - Method Inlining: оптимизация компилятора — подстановка тела метода в место вызова; для
finalэто особенно реалистично. - Interface (language construct): в Java — тип, задающий контракт сигнатур без реализации; класс может реализовать несколько интерфейсов.
- Interface Implementation: предоставление тел для всех методов интерфейса с помощью
implements. - Multiple Interface Implementation: один класс реализует несколько интерфейсов — безопасная альтернатива множественному наследованию реализации.
- Interface Inheritance: интерфейс расширяет другой интерфейс, наследуя объявления методов.
- Facet: «грань» или представление объекта (часто через интерфейс), показывающее лишь часть возможностей.
- Empty Interface (Marker Interface): интерфейс без методов — маркер свойства класса.
- Nested Interface: интерфейс, объявленный внутри класса или другого интерфейса.
- Ad-hoc Polymorphism (Duck Typing): обращение с объектом по набору методов без явного объявления типа; в Java напрямую не поддерживается.
- Enumeration (Enum): тип с фиксированным набором именованных констант (enumerators); повышает типобезопасность категориальных значений.
- Enumerator: именованная константа внутри перечисления.
- Enum Constructor: конструктор в Java-enum, инициализирующий каждую константу.
- Enum Methods: методы enum, задающие поведение констант.
- Collection: структура для группы объектов с операциями добавления, удаления и доступа.
- List: упорядоченная коллекция с доступом по индексу; допускает дубликаты и обычно сохраняет порядок вставки.
- Set: коллекция без дубликатов; не больше одного
null. - Map: пары ключ–значение; каждый ключ уникален и отображается не более чем в одно значение.
- ArrayList: реализация
Listна динамическом массиве; быстрый произвольный доступ. - LinkedList: двусвязный список; реализует
ListиDeque; удобные вставки/удаления. - HashSet: множество на хеш-таблице; амортизированно \(O(1)\) для базовых операций.
- HashMap: отображение на хеш-таблице; амортизированно \(O(1)\) для поиска по ключу.
- TreeSet: множество на красно-чёрном дереве; элементы в отсортированном порядке.
- TreeMap: отображение на красно-чёрном дереве; ключи упорядочены.
- Iterator: объект обхода коллекции с
hasNext/next/remove. - Generic Type Parameter: параметр типа (
<E>,<K,V>), обеспечивающий проверку типов на этапе компиляции. - Entry (Map.Entry): пара ключ–значение при обходе
entrySet()уMap. - UML (Unified Modeling Language): стандартизованный язык визуального моделирования ПО.
- UML Class Diagram: диаграмма классов: поля, методы и связи между классами.
- Association (UML): связь «класс использует другой класс».
- Aggregation (UML): «has-a», часть может существовать отдельно; пустой ромб.
- Composition (UML): «has-a», часть не существует отдельно от целого; закрашенный ромб.
- Multiplicity (UML): кратность связи (
1,*,0..1,1..*).
3. Примеры
3.1. Поведение конструкторов enum (Лаба 10, Задание 1)
Покажите, когда вызываются конструкторы enum.
Нажмите, чтобы увидеть решение
Ключевая идея: конструкторы enum вызываются при первом обращении к типу, по одному разу на каждую константу.
enum Color {
RED,
GREEN,
BLUE;
private Color() {
System.out.println("Constructor called for: " + this.toString());
}
public void colorInfo() {
System.out.println("Universal Color");
}
}
public class Test {
public static void main(String[] args) {
System.out.println("Before accessing enum");
Color c1 = Color.RED;
System.out.println(c1);
c1.colorInfo();
System.out.println("\nAccessing another color:");
Color c2 = Color.GREEN;
System.out.println(c2);
}
}Вывод:
Before accessing enum
Constructor called for: RED
Constructor called for: GREEN
Constructor called for: BLUE
RED
Universal Color
Accessing another color:
GREEN
Пояснение: все константы enum создаются при первом обращении к типу, а не «лениво» при первом обращении к каждой константе по отдельности.
Ответ: конструктор enum вызывается по разу на каждую константу при загрузке класса enum — при первом обращении к любой константе или методу типа.
3.2. Программа со списком животных (Лаба 10, Задание 3)
Напишите программу: список животных и методы добавления, удаления, обновления и вывода.
Нажмите, чтобы увидеть решение
Ключевая идея: инкапсулируйте операции со списком в методы — проще сопровождать и переиспользовать.
import java.util.ArrayList;
import java.util.List;
public class AnimalManager {
private List<String> animals;
public AnimalManager() {
animals = new ArrayList<>();
}
// Method to add an animal
public void addAnimal(String animal) {
animals.add(animal);
System.out.println("Added: " + animal);
}
// Method to remove an animal
public void removeAnimal(String animal) {
if (animals.remove(animal)) {
System.out.println("Removed: " + animal);
} else {
System.out.println("Animal not found: " + animal);
}
}
// Method to update an animal at a specific index
public void updateAnimal(int index, String newAnimal) {
if (index >= 0 && index < animals.size()) {
String old = animals.set(index, newAnimal);
System.out.println("Updated: " + old + " -> " + newAnimal);
} else {
System.out.println("Invalid index: " + index);
}
}
// Method to display all animals
public void displayAnimals() {
System.out.println("\nCurrent animals:");
if (animals.isEmpty()) {
System.out.println(" (no animals)");
} else {
for (int i = 0; i < animals.size(); i++) {
System.out.println(" " + i + ": " + animals.get(i));
}
}
}
public static void main(String[] args) {
AnimalManager manager = new AnimalManager();
// Adding animals
manager.addAnimal("Lion");
manager.addAnimal("Tiger");
manager.addAnimal("Bear");
manager.addAnimal("Elephant");
manager.displayAnimals();
// Updating an animal
manager.updateAnimal(1, "Leopard");
manager.displayAnimals();
// Removing an animal
manager.removeAnimal("Bear");
manager.displayAnimals();
// Try removing non-existent animal
manager.removeAnimal("Giraffe");
}
}Вывод:
Added: Lion
Added: Tiger
Added: Bear
Added: Elephant
Current animals:
0: Lion
1: Tiger
2: Bear
3: Elephant
Updated: Tiger -> Leopard
Current animals:
0: Lion
1: Leopard
2: Bear
3: Elephant
Removed: Bear
Current animals:
0: Lion
1: Leopard
2: Elephant
Animal not found: Giraffe
Ответ: четыре операции над List — add/remove/update/display; методы дают обратную связь и обрабатывают ошибки.
3.3. Удаление из Set строк нечётной длины (Лаба 10, Задание 4)
Напишите программу: множество строк, удалить все элементы нечётной длины, оставить только чётной длины.
Нажмите, чтобы увидеть решение
Ключевая идея: при удалении во время обхода используйте Iterator, чтобы избежать ConcurrentModificationException.
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
public class StringSetFilter {
public static void main(String[] args) {
// Create a Set with strings
Set<String> strings = new HashSet<>();
strings.add("cat"); // length 3 (odd)
strings.add("dog"); // length 3 (odd)
strings.add("bird"); // length 4 (even)
strings.add("fish"); // length 4 (even)
strings.add("elephant"); // length 8 (even)
strings.add("tiger"); // length 5 (odd)
strings.add("bear"); // length 4 (even)
System.out.println("Original set: " + strings);
// Remove odd-length strings using Iterator
Iterator<String> it = strings.iterator();
while (it.hasNext()) {
String str = it.next();
if (str.length() % 2 != 0) { // Odd length
System.out.println("Removing: " + str + " (length " + str.length() + ")");
it.remove(); // Safe removal using iterator
}
}
System.out.println("Final set (even lengths only): " + strings);
}
}Возможный вывод:
Original set: [cat, bear, bird, fish, dog, elephant, tiger]
Removing: cat (length 3)
Removing: dog (length 3)
Removing: tiger (length 5)
Final set (even lengths only): [bear, bird, fish, elephant]
Замечание: Order may vary since HashSet is unordered.
Альтернативное решение using removeIf (Java 8+):
import java.util.Set;
import java.util.HashSet;
public class StringSetFilterLambda {
public static void main(String[] args) {
Set<String> strings = new HashSet<>();
strings.add("cat");
strings.add("dog");
strings.add("bird");
strings.add("fish");
strings.add("elephant");
strings.add("tiger");
strings.add("bear");
System.out.println("Original set: " + strings);
// Remove odd-length strings using removeIf
strings.removeIf(str -> str.length() % 2 != 0);
System.out.println("Final set (even lengths only): " + strings);
}
}Ответ: для удаления при обходе используйте Iterator.remove() — без ConcurrentModificationException. В Java 8 короче записывается removeIf().
3.4. Map и повторяющиеся значения (Лаба 10, Задание 5)
Напишите программу: Map<String, Integer> из ввода пользователя и отчёт о повторяющихся значениях и их количестве.
Нажмите, чтобы увидеть решение
Ключевая идея: используйте Map для подсчёта вхождений, затем анализируйте значения на повторы.
import java.util.Map;
import java.util.HashMap;
import java.util.Scanner;
public class RepetitiveValueCounter {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Map<String, Integer> wordCount = new HashMap<>();
System.out.println("Enter words (type 'done' to finish):");
// Read input and build the map
while (true) {
String word = scanner.next();
if (word.equalsIgnoreCase("done")) {
break;
}
// Count occurrences
wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
}
System.out.println("\nWord counts:");
for (Map.Entry<String, Integer> entry : wordCount.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Find and report repetitive values
System.out.println("\nAnalysis:");
boolean hasRepetitions = false;
for (Map.Entry<String, Integer> entry : wordCount.entrySet()) {
if (entry.getValue() > 1) {
hasRepetitions = true;
System.out.println("'" + entry.getKey() + "' appears " +
entry.getValue() + " times (repetitive)");
}
}
if (!hasRepetitions) {
System.out.println("No repetitive values found.");
}
scanner.close();
}
}Пример запуска:
Enter words (type 'done' to finish):
apple banana apple orange banana apple done
Word counts:
apple: 3
banana: 2
orange: 1
Analysis:
'apple' appears 3 times (repetitive)
'banana' appears 2 times (repetitive)
Альтернативная интерпретация (checking if different keys map to same value):
import java.util.Map;
import java.util.HashMap;
import java.util.Scanner;
import java.util.List;
import java.util.ArrayList;
public class RepetitiveValueChecker {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Map<String, Integer> map = new HashMap<>();
System.out.println("Enter key-value pairs (format: key value)");
System.out.println("Type 'done' as key to finish:");
// Read key-value pairs
while (true) {
System.out.print("Key: ");
String key = scanner.next();
if (key.equalsIgnoreCase("done")) {
break;
}
System.out.print("Value: ");
int value = scanner.nextInt();
map.put(key, value);
}
System.out.println("\nMap contents: " + map);
// Check for repetitive values
Map<Integer, List<String>> valueToKeys = new HashMap<>();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
int value = entry.getValue();
valueToKeys.putIfAbsent(value, new ArrayList<>());
valueToKeys.get(value).add(entry.getKey());
}
System.out.println("\nAnalysis:");
boolean hasRepetitions = false;
for (Map.Entry<Integer, List<String>> entry : valueToKeys.entrySet()) {
if (entry.getValue().size() > 1) {
hasRepetitions = true;
System.out.println("Value " + entry.getKey() +
" is mapped by " + entry.getValue().size() +
" keys: " + entry.getValue());
}
}
if (!hasRepetitions) {
System.out.println("No repetitive values found.");
}
scanner.close();
}
}Пример запуска:
Enter key-value pairs (format: key value)
Type 'done' as key to finish:
Key: apple
Value: 100
Key: banana
Value: 200
Key: orange
Value: 100
Key: grape
Value: 200
Key: done
Map contents: {apple=100, banana=200, orange=100, grape=200}
Analysis:
Value 100 is mapped by 2 keys: [apple, orange]
Value 200 is mapped by 2 keys: [banana, grape]
Ответ: первый вариант считает частоту слов через Map; второй ищет одинаковые значения у разных ключей через «обращение» структуры.
3.5. Система онлайн-чтения книг (Лаба 10, Домашнее задание 1)
Спроектируйте и реализуйте базовую систему онлайн-чтения книг со следующими требованиями:
Функциональность:
- поиск по базе книг и чтение книги
- создание и продление подписки пользователя
- одновременно только один активный пользователь и только одна активная книга у него
Подсказки по проектированию:
Класс OnlineReaderSystem — точка входа. Не помещайте всё в один класс; разнесите ответственность:
Library: коллекция книгUserManager: пользователиDisplay: вывод / UIBook: сущность книгиUser: сущность пользователя
Замечание: похожие задачи встречаются на собеседованиях в Amazon, Microsoft и др.
Нажмите, чтобы увидеть решение
Ключевая идея: разделяйте ответственность по ОО-принципам; у каждого класса — одна чёткая роль.
Шаг 1: UML-диаграмма классов
Перед кодом спланируйте классы и связи:
OnlineReaderSystemaggregatesLibrary,UserManager, andDisplayLibrarycontains a collection ofBookobjectsUserManagermanages a collection ofUserobjectsUserhas an association withBook(current book being read)
Шаг 2: реализация
import java.util.*;
// Book class
class Book {
private String id;
private String title;
private String author;
private String content;
public Book(String id, String title, String author, String content) {
this.id = id;
this.title = title;
this.author = author;
this.content = content;
}
public String getId() { return id; }
public String getTitle() { return title; }
public String getAuthor() { return author; }
public String getContent() { return content; }
@Override
public String toString() {
return title + " by " + author;
}
}
// User class
class User {
private String userId;
private String name;
private String membershipType;
private Book currentBook;
public User(String userId, String name, String membershipType) {
this.userId = userId;
this.name = name;
this.membershipType = membershipType;
this.currentBook = null;
}
public String getUserId() { return userId; }
public String getName() { return name; }
public String getMembershipType() { return membershipType; }
public Book getCurrentBook() { return currentBook; }
public void setCurrentBook(Book book) {
this.currentBook = book;
}
public void extendMembership(String newType) {
this.membershipType = newType;
System.out.println("Membership extended to: " + newType);
}
@Override
public String toString() {
return name + " (ID: " + userId + ", " + membershipType + ")";
}
}
// Library class - manages books
class Library {
private Map<String, Book> books;
public Library() {
books = new HashMap<>();
}
public void addBook(Book book) {
books.put(book.getId(), book);
System.out.println("Added book: " + book);
}
public Book findBook(String id) {
return books.get(id);
}
public List<Book> searchBooks(String keyword) {
List<Book> results = new ArrayList<>();
for (Book book : books.values()) {
if (book.getTitle().toLowerCase().contains(keyword.toLowerCase()) ||
book.getAuthor().toLowerCase().contains(keyword.toLowerCase())) {
results.add(book);
}
}
return results;
}
public void displayAllBooks() {
System.out.println("\n=== Library Books ===");
for (Book book : books.values()) {
System.out.println(book);
}
}
}
// UserManager class - manages users
class UserManager {
private Map<String, User> users;
private User activeUser;
public UserManager() {
users = new HashMap<>();
activeUser = null;
}
public User createUser(String userId, String name, String membershipType) {
User user = new User(userId, name, membershipType);
users.put(userId, user);
System.out.println("User created: " + user);
return user;
}
public User findUser(String userId) {
return users.get(userId);
}
public boolean setActiveUser(String userId) {
User user = users.get(userId);
if (user != null) {
activeUser = user;
System.out.println("Active user set to: " + user);
return true;
}
System.out.println("User not found: " + userId);
return false;
}
public User getActiveUser() {
return activeUser;
}
public void logoutActiveUser() {
if (activeUser != null) {
System.out.println("User logged out: " + activeUser);
activeUser = null;
}
}
}
// Display class - manages display output
class Display {
private Book activeBook;
private User activeUser;
public void displayBook(Book book) {
if (book == null) {
System.out.println("No book to display.");
return;
}
activeBook = book;
System.out.println("\n========== READING ==========");
System.out.println("Title: " + book.getTitle());
System.out.println("Author: " + book.getAuthor());
System.out.println("-----------------------------");
System.out.println(book.getContent());
System.out.println("=============================\n");
}
public void displayUser(User user) {
if (user == null) {
System.out.println("No active user.");
return;
}
activeUser = user;
System.out.println("\n=== Active User ===");
System.out.println(user);
if (user.getCurrentBook() != null) {
System.out.println("Currently reading: " + user.getCurrentBook());
} else {
System.out.println("Not reading any book.");
}
System.out.println("===================\n");
}
public void refreshDisplay(User user, Book book) {
displayUser(user);
if (book != null) {
displayBook(book);
}
}
}
// Main OnlineReaderSystem class
class OnlineReaderSystem {
private Library library;
private UserManager userManager;
private Display display;
public OnlineReaderSystem() {
library = new Library();
userManager = new UserManager();
display = new Display();
}
public Library getLibrary() { return library; }
public UserManager getUserManager() { return userManager; }
public Display getDisplay() { return display; }
public void setActiveUserAndBook(String userId, String bookId) {
User user = userManager.findUser(userId);
if (user == null) {
System.out.println("User not found.");
return;
}
userManager.setActiveUser(userId);
Book book = library.findBook(bookId);
if (book == null) {
System.out.println("Book not found.");
return;
}
user.setCurrentBook(book);
display.refreshDisplay(user, book);
}
public void searchAndDisplay(String keyword) {
List<Book> results = library.searchBooks(keyword);
System.out.println("\n=== Search Results for: " + keyword + " ===");
if (results.isEmpty()) {
System.out.println("No books found.");
} else {
for (Book book : results) {
System.out.println(book);
}
}
}
public static void main(String[] args) {
OnlineReaderSystem system = new OnlineReaderSystem();
// Add books to library
system.getLibrary().addBook(new Book("B001", "Java Programming",
"John Smith", "This is a comprehensive guide to Java..."));
system.getLibrary().addBook(new Book("B002", "Data Structures",
"Jane Doe", "Learn about arrays, lists, trees..."));
system.getLibrary().addBook(new Book("B003", "Design Patterns",
"Bob Johnson", "Explore common software design patterns..."));
// Display all books
system.getLibrary().displayAllBooks();
// Create users
system.getUserManager().createUser("U001", "Alice", "Premium");
system.getUserManager().createUser("U002", "Bob", "Basic");
// Set active user and read a book
System.out.println("\n--- Alice starts reading ---");
system.setActiveUserAndBook("U001", "B001");
// Search for books
system.searchAndDisplay("Data");
// Extend membership
User alice = system.getUserManager().findUser("U001");
alice.extendMembership("Gold");
// Logout
system.getUserManager().logoutActiveUser();
// Another user
System.out.println("\n--- Bob starts reading ---");
system.setActiveUserAndBook("U002", "B003");
}
}Вывод:
Added book: Java Programming by John Smith
Added book: Data Structures by Jane Doe
Added book: Design Patterns by Bob Johnson
=== Library Books ===
Java Programming by John Smith
Data Structures by Jane Doe
Design Patterns by Bob Johnson
User created: Alice (ID: U001, Premium)
User created: Bob (ID: U002, Basic)
--- Alice starts reading ---
Active user set to: Alice (ID: U001, Premium)
=== Active User ===
Alice (ID: U001, Premium)
Currently reading: Java Programming by John Smith
===================
========== READING ==========
Title: Java Programming
Author: John Smith
-----------------------------
This is a comprehensive guide to Java...
=============================
=== Search Results for: Data ===
Data Structures by Jane Doe
Membership extended to: Gold
User logged out: Alice (ID: U001, Gold)
--- Bob starts reading ---
Active user set to: Bob (ID: U002, Basic)
=== Active User ===
Bob (ID: U002, Basic)
Currently reading: Design Patterns by Bob Johnson
===================
========== READING ==========
Title: Design Patterns
Author: Bob Johnson
-----------------------------
Explore common software design patterns...
=============================
Применённые принципы проектирования:
- Single Responsibility Principle: у каждого класса одна ясная роль
Library: каталог книг
UserManager: пользователи
Display: вывод / UI
OnlineReaderSystem: координация
- Encapsulation: данные класса скрыты полями
private, доступ через публичные методы - Aggregation:
OnlineReaderSystemагрегируетLibrary,UserManagerиDisplay - Association: связь
User↔︎Book(объекты существуют независимо)
Ответ: разделение ответственности упрощает сопровождение и расширение; ограничение «один активный пользователь и одна книга» задаётся через UserManager и User.
3.6. Разделение interface и implementation (Лекция 10, Пример 1)
Иерархия самолётов с разными алгоритмами полёта: реализуйте корректно, разделяя interface и implementation.
Нажмите, чтобы увидеть решение
Ключевая идея: abstract заставляет явно реализовать метод; общий код вынесите в protected-хелпер.
Неверный подход (в чём проблема):
class Airplane {
public void fly() {
// Standard flying algorithm for Airbus
}
}
class AirbusA extends Airplane { } // Inherits Airbus algorithm
class AirbusB extends Airplane { } // Inherits Airbus algorithm
class Boeing extends Airplane { } // WRONG: Also inherits Airbus algorithm!Проблема: Boeing унаследовал алгоритм Airbus; синтаксис верный, ошибка семантическая.
Правильный подход (решение):
abstract class Airplane {
// Interface: must be implemented by all subclasses
public abstract void fly();
// Optional default implementation
protected void defaultFly() {
// Standard flying algorithm
System.out.println("Standard flying algorithm");
}
}
class AirbusA extends Airplane {
public void fly() {
defaultFly(); // Can choose to use default
}
}
class AirbusB extends Airplane {
public void fly() {
defaultFly(); // Can choose to use default
}
}
class Boeing extends Airplane {
public void fly() {
// MUST provide its own implementation
System.out.println("Boeing's own flying algorithm");
}
}Использование:
Airplane a = new AirbusA();
a.fly(); // Standard flying algorithm
Airplane b = new AirbusB();
b.fly(); // Standard flying algorithm
Airplane c = new Boeing();
c.fly(); // Boeing's own flying algorithmОтвет: abstract void fly() заставляет каждый подкласс явно реализовать полёт; defaultFly() даёт опциональную общую реализацию.
3.7. Использование final-методов (Лекция 10, Пример 2)
Создайте базовый класс с final-методом и покажите, что его нельзя переопределить.
Нажмите, чтобы увидеть решение
Ключевая идея: final у метода запрещает переопределение и стабилизирует поведение.
class Base {
public final void method() {
System.out.println("Base's method");
}
}
class Derived extends Base {
// This would cause a compilation error:
// public void method() {
// System.out.println("Derived's method");
// }
// Error: Cannot override the final method from Base
}Проверка кода:
Base b1 = new Base();
b1.method(); // Output: Base's method
Base b2 = new Derived();
b2.method(); // Output: Base's method (same implementation)Ответ: final-методы фиксируют поведение базового класса и помогают компилятору оптимизировать вызовы.
3.8. Создание final-класса (Лекция 10, Пример 3)
Покажите final-класс и невозможность от него наследоваться.
Нажмите, чтобы увидеть решение
final class Base {
public void method() {
System.out.println("Base's method");
}
}
// This would cause a compilation error:
// class Derived extends Base {
// }
// Error: The type Derived cannot subclass the final class BaseWhy can’t a class be both abstract and final?
// This is illegal and won't compile:
// abstract final class InvalidClass {
// abstract void method();
// }Пояснение: An abstract class is incomplete by design—it requires subclasses to provide implementations. A final class forbids subclasses. These two concepts are contradictory, so using both is illegal.
Ответ: final-класс нельзя расширять; все его методы неявно final; abstract и final вместе недопустимы.
3.9. Реализация интерфейса (Лекция 10, Пример 4)
Создайте интерфейс Features и класс Lion, который его реализует.
Нажмите, чтобы увидеть решение
Ключевая идея: интерфейс задаёт контракт; класс обязан реализовать все объявленные методы.
interface Features {
int numOfLegs();
boolean canFly();
boolean canSwim();
}
class Lion implements Features {
public int numOfLegs() {
return 4;
}
public boolean canFly() {
return false;
}
public boolean canSwim() {
return true;
}
}Использование:
// Can treat Lion as a Features interface
Features f = new Lion();
System.out.println("Number of legs: " + f.numOfLegs());
if (f.canFly()) {
System.out.println("Can fly");
} else {
System.out.println("Cannot fly");
}
if (f.canSwim()) {
System.out.println("Can swim");
}Вывод:
Number of legs: 4
Cannot fly
Can swim
Ответ: Lion реализует все три метода Features; ссылку типа Features можно направить на экземпляр Lion.
3.10. Несколько интерфейсов у одного класса (Лекция 10, Пример 5)
Создайте класс Person, реализующий несколько интерфейсов — разные аспекты одного человека.
Нажмите, чтобы увидеть решение
Ключевая идея: класс может реализовать несколько интерфейсов — безопасная альтернатива множественному наследованию.
interface iBodyParams {
int getHeight();
int getWeight();
}
interface iSkills {
String getProgrammingLanguage();
int getExperienceYears();
}
interface iRelations {
String getFriendName();
}
class Person implements iBodyParams, iSkills, iRelations {
private int height;
private int weight;
private String programmingLanguage;
private int experienceYears;
private String friendName;
public Person(int height, int weight, String language,
int experience, String friend) {
this.height = height;
this.weight = weight;
this.programmingLanguage = language;
this.experienceYears = experience;
this.friendName = friend;
}
// iBodyParams implementation
public int getHeight() { return height; }
public int getWeight() { return weight; }
// iSkills implementation
public String getProgrammingLanguage() { return programmingLanguage; }
public int getExperienceYears() { return experienceYears; }
// iRelations implementation
public String getFriendName() { return friendName; }
}Использование (разные «ракурсы»):
Person john = new Person(180, 75, "Java", 5, "Alice");
// View John as a set of skills
iSkills johnsSkills = john;
System.out.println("Programming language: " + johnsSkills.getProgrammingLanguage());
System.out.println("Experience: " + johnsSkills.getExperienceYears() + " years");
// View John's body parameters
iBodyParams johnsBody = john;
System.out.println("Height: " + johnsBody.getHeight() + " cm");
// View John's relationships
iRelations johnsRelations = john;
System.out.println("Friend: " + johnsRelations.getFriendName());Ответ: один Person можно рассматривать через разные интерфейсы — разные facets.
3.11. Наследование интерфейсов (Лекция 10, Пример 6)
Покажите расширение интерфейсов и влияние на реализующие классы.
Нажмите, чтобы увидеть решение
Ключевая идея: интерфейс может расширять другой интерфейс, наследуя все объявления методов.
interface SpeedFeatures {
float maxSpeed();
float maxAcceleration();
}
interface EngineFeatures extends SpeedFeatures {
float numOfCyls();
float enginePower();
}
class Car implements EngineFeatures {
// Must implement ALL methods from both interfaces
// From SpeedFeatures
public float maxSpeed() {
return 200.0f;
}
public float maxAcceleration() {
return 5.5f;
}
// From EngineFeatures
public float numOfCyls() {
return 6.0f;
}
public float enginePower() {
return 300.0f;
}
}Использование:
Car myCar = new Car();
// Can be treated as EngineFeatures
EngineFeatures engine = myCar;
System.out.println("Engine power: " + engine.enginePower() + " HP");
System.out.println("Cylinders: " + engine.numOfCyls());
// Can also be treated as SpeedFeatures
SpeedFeatures speed = myCar;
System.out.println("Max speed: " + speed.maxSpeed() + " km/h");
System.out.println("Max acceleration: " + speed.maxAcceleration() + " m/s²");Ответ: Car реализует методы и SpeedFeatures, и EngineFeatures, потому что EngineFeatures расширяет SpeedFeatures; объект можно рассматривать через любой из этих типов.
3.12. Проверка типов с интерфейсами (Лекция 10, Пример 7)
Используйте instanceof, чтобы проверить реализацию интерфейсов и выполнить нужные действия.
Нажмите, чтобы увидеть решение
Ключевая идея: instanceof работает с интерфейсами и проверяет, реализует ли объект данный интерфейс.
interface Printable {
void print();
}
interface Movable {
void move();
}
interface Serializable {
void serialize();
}
abstract class Shape {
abstract void draw();
}
class Rectangle extends Shape implements Printable, Movable, Serializable {
public void draw() {
System.out.println("Drawing rectangle");
}
public void print() {
System.out.println("Printing rectangle");
}
public void move() {
System.out.println("Moving rectangle");
}
public void serialize() {
System.out.println("Serializing rectangle");
}
}Использование:
Shape a = new Rectangle();
// Check and use different interfaces
if (a instanceof Printable) {
((Printable)a).print(); // Prints: Printing rectangle
}
if (a instanceof Movable) {
((Movable)a).move(); // Prints: Moving rectangle
}
if (a instanceof Serializable) {
((Serializable)a).serialize(); // Prints: Serializing rectangle
}
// This would be false
if (a instanceof String) {
System.out.println("Never executes");
}Ответ: instanceof проверяет, реализует ли класс объекта интерфейс; после проверки безопасно приводить ссылку к типу интерфейса.
3.13. Вложенный интерфейс (Лекция 10, Пример 8)
Создайте вложенный интерфейс внутри класса и реализуйте его.
Нажмите, чтобы увидеть решение
class SomeClass {
public interface Nested {
boolean isNotNegative(int x);
}
}
class MyClass implements SomeClass.Nested {
public boolean isNotNegative(int x) {
return x >= 0;
}
}
class Demo {
public static void main(String[] args) {
SomeClass.Nested obj = new MyClass();
if (obj.isNotNegative(10)) {
System.out.println("10 is not negative");
}
if (obj.isNotNegative(-5)) {
System.out.println("Never prints");
} else {
System.out.println("-5 is negative");
}
}
}Вывод:
10 is not negative
-5 is negative
Ответ: к вложенному интерфейсу обращаются как SomeClass.Nested; реализовать может любой класс.
3.14. Базовое перечисление (Лекция 10, Пример 9)
Создайте перечисление дней недели и используйте его в switch.
Нажмите, чтобы увидеть решение
Ключевая идея: enum даёт типобезопасные константы и естественно сочетается с switch.
public enum Day {
SUNDAY,
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY
}
public class DayDemo {
public void tellItLikeItIs(Day day) {
switch (day) {
case MONDAY:
System.out.println("Mondays are bad.");
break;
case FRIDAY:
System.out.println("Fridays are better.");
break;
case SATURDAY:
case SUNDAY:
System.out.println("Weekends are best.");
break;
default:
System.out.println("Midweek days are so-so.");
break;
}
}
public static void main(String[] args) {
DayDemo demo = new DayDemo();
demo.tellItLikeItIs(Day.MONDAY); // Mondays are bad.
demo.tellItLikeItIs(Day.FRIDAY); // Fridays are better.
demo.tellItLikeItIs(Day.SATURDAY); // Weekends are best.
demo.tellItLikeItIs(Day.WEDNESDAY); // Midweek days are so-so.
}
}Ответ: enum даёт читаемые типобезопасные константы; в tellItLikeItIs передаются только корректные Day.
3.15. Enum с полями и конструктором (Лекция 10, Пример 10)
Создайте перечисление Coin со значениями в центах и методами.
Нажмите, чтобы увидеть решение
Ключевая идея: в Java enum — класс: поля, конструкторы и методы мощнее «простых» констант.
enum Coin {
PENNY(1),
NICKEL(5),
DIME(10),
QUARTER(25);
private final int value;
// Constructor is private by default
Coin(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public class CoinDemo {
public static void main(String[] args) {
Coin c = Coin.DIME;
System.out.println("A dime is worth " + c.getValue() + " cents");
// Print all coins and their values
System.out.println("\nAll coins:");
for (Coin coin : Coin.values()) {
System.out.println(coin + ": " + coin.getValue() + " cents");
}
}
}Вывод:
A dime is worth 10 cents
All coins:
PENNY: 1 cents
NICKEL: 5 cents
DIME: 10 cents
QUARTER: 25 cents
Ответ: конструктор задаёт значения констант; values() возвращает массив констант в порядке объявления.
3.16. Enum с разными телами методов у констант (Лекция 10, Пример 11)
Создайте Operation, у каждой константы — своя реализация eval().
Нажмите, чтобы увидеть решение
Ключевая идея: у каждой константы enum может быть своё тело метода — поведение зависит от константы.
enum Operation {
PLUS {
double eval(double x, double y) {
return x + y;
}
},
MINUS {
double eval(double x, double y) {
return x - y;
}
},
TIMES {
double eval(double x, double y) {
return x * y;
}
},
DIVIDE {
double eval(double x, double y) {
return x / y;
}
};
// Abstract method that each constant must implement
abstract double eval(double x, double y);
public static void main(String[] args) {
double x = 2.0;
double y = 4.0;
for (Operation op : Operation.values()) {
System.out.println(x + " " + op + " " + y + " = " + op.eval(x, y));
}
}
}Вывод:
2.0 PLUS 4.0 = 6.0
2.0 MINUS 4.0 = -2.0
2.0 TIMES 4.0 = 8.0
2.0 DIVIDE 4.0 = 0.5
Ответ: у каждой константы своё тело eval() — альтернатива большому switch.
3.17. Методы values() и ordinal() (Туториал 10, Задание 1)
Покажите стандартные методы values() и ordinal() у любого enum.
Нажмите, чтобы увидеть решение
Ключевая идея: у любого Java-enum есть values() (все константы) и ordinal() (индекс).
public class Test {
enum Season {
WINTER,
SPRING,
SUMMER,
FALL
}
public static void main(String[] args) {
System.out.println("All seasons:");
for (Season s : Season.values()) {
System.out.println(s);
}
System.out.println("\nSeasons with ordinals:");
for (Season s : Season.values()) {
System.out.println(s + " has ordinal " + s.ordinal());
}
System.out.println("\nOrdinal of SUMMER: " + Season.SUMMER.ordinal());
}
}Вывод:
All seasons:
WINTER
SPRING
SUMMER
FALL
Seasons with ordinals:
WINTER has ordinal 0
SPRING has ordinal 1
SUMMER has ordinal 2
FALL has ordinal 3
Ordinal of SUMMER: 2
Ответ: values() — массив констант по порядку объявления; ordinal() — нулевой индекс позиции.
3.18. Создание и использование ArrayList (Туториал 10, Задание 2)
Создайте ArrayList строк и выполните базовые операции.
Нажмите, чтобы увидеть решение
Ключевая идея: ArrayList is a resizable array implementation of the List interface.
import java.util.List;
import java.util.ArrayList;
public class GFG {
public static void main(String args[]) {
// Creating an ArrayList
List<String> al = new ArrayList<>();
// Adding elements
al.add("Geeks");
al.add("Geeks");
al.add(1, "For"); // Insert at index 1
// Print all elements
System.out.println(al);
// Access by index
System.out.println("Element at index 0: " + al.get(0));
System.out.println("Element at index 1: " + al.get(1));
// Size
System.out.println("Size: " + al.size());
// Remove
al.remove(2); // Remove element at index 2
System.out.println("After removal: " + al);
}
}Вывод:
[Geeks, For, Geeks]
Element at index 0: Geeks
Element at index 1: For
Size: 3
After removal: [Geeks, For]
Ответ: ArrayList сохраняет порядок вставки и дубликаты; индексация с нуля.
3.19. Создание и использование HashSet (Туториал 10, Задание 3)
Создайте HashSet и покажите, что дубликаты отбрасываются.
Нажмите, чтобы увидеть решение
Ключевая идея: HashSet stores unique elements only. Attempting to add a duplicate has no effect.
import java.util.Set;
import java.util.HashSet;
public class GFG {
public static void main(String[] args) {
// Create a HashSet
Set<String> hashSet = new HashSet<>();
// Adding elements
hashSet.add("Geeks");
hashSet.add("For");
hashSet.add("Geeks"); // Duplicate - will be ignored
hashSet.add("Example");
hashSet.add("Set");
// Print (order not guaranteed)
System.out.println(hashSet);
// Check if contains
System.out.println("Contains 'Geeks': " + hashSet.contains("Geeks"));
System.out.println("Contains 'Java': " + hashSet.contains("Java"));
// Size
System.out.println("Size: " + hashSet.size());
}
}Возможный вывод:
[Geeks, Example, Set, For]
Contains 'Geeks': true
Contains 'Java': false
Size: 4
Замечание: порядок элементов в выводе HashSet не определён.
Ответ: HashSet отбрасывает дубликаты; второй Geeks не добавится — в множестве 4 элемента.
3.20. Создание и использование HashMap (Туториал 10, Задание 4)
Создайте HashMap, сохраните пары ключ–значение и обойдите его.
Нажмите, чтобы увидеть решение
Ключевая идея: HashMap stores key-value pairs where each unique key maps to one value.
import java.util.Map;
import java.util.HashMap;
public class HashMapDemo {
public static void main(String args[]) {
// Create a HashMap
Map<String, Integer> hm = new HashMap<>();
// Adding key-value pairs
hm.put("a", 100);
hm.put("b", 200);
hm.put("c", 300);
hm.put("d", 400);
// Replacing a value
hm.put("a", 150); // Replaces previous value for key "a"
System.out.println("HashMap contents:");
// Traversing through the map
for (Map.Entry<String, Integer> me : hm.entrySet()) {
System.out.println(me.getKey() + ": " + me.getValue());
}
// Access by key
System.out.println("\nValue for key 'b': " + hm.get("b"));
// Check if key exists
System.out.println("Contains key 'c': " + hm.containsKey("c"));
System.out.println("Contains key 'z': " + hm.containsKey("z"));
// Size
System.out.println("Size: " + hm.size());
}
}Возможный вывод:
HashMap contents:
a: 150
b: 200
c: 300
d: 400
Value for key 'b': 200
Contains key 'c': true
Contains key 'z': false
Size: 4
Замечание: порядок пар в выводе HashMap не определён (без LinkedHashMap).
Ответ: в HashMap ключи уникальны; повторный put заменяет значение; обход через entrySet().
3.21. Использование Iterator (Туториал 10, Задание 5)
Обойдите ArrayList с помощью Iterator.
Нажмите, чтобы увидеть решение
Ключевая идея: Iterator — единообразный обход любой коллекции.
import java.util.ArrayList;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
// Create a collection
ArrayList<String> cars = new ArrayList<>();
cars.add("Volvo");
cars.add("BMW");
cars.add("Ford");
cars.add("Mazda");
// Get the iterator
Iterator<String> it = cars.iterator();
// Print all elements using iterator
System.out.println("All cars:");
while (it.hasNext()) {
String car = it.next();
System.out.println(car);
}
// Create a new iterator for another traversal
Iterator<String> it2 = cars.iterator();
// Print the first item
System.out.println("\nFirst car: " + it2.next());
}
}Вывод:
All cars:
Volvo
BMW
Ford
Mazda
First car: Volvo
Ответ: iterator() выдаёт Iterator; hasNext/next для обхода; после прохода нужен новый итератор.